﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;

namespace MyGames
{
    public enum GestureDirection
    {
        Up,
        Down,
        Right,
        Left,
        None
    }

    public static class ExtensionMethods
    {
        public static GestureDirection GetDirection(this GestureSample gesture)
        {
            GestureDirection direction = GestureDirection.None;

            if (gesture.Delta.Y != 0)
            {
                if (gesture.Delta.Y > 0)
                {
                    direction = GestureDirection.Down;
                }
                else
                {
                    direction = GestureDirection.Up;
                }
            }
            else if (gesture.Delta.X != 0)
            {
                if (gesture.Delta.X > 0)
                {
                    direction = GestureDirection.Right;
                }
                else
                {
                    direction = GestureDirection.Left;
                }
            }
            return direction;
        }
    }

    static public class InputManager
    {
        /// <summary>
        /// This class handles all keyboard, mouse and gamepad actions in the game.
        /// </summary>

        #region Action Enumeration


        /// <summary>
        /// The actions that are possible within the game.
        /// </summary>
        public enum Action
        {
            // Nagivation (through menu, screen.. with keyboard)
            NagivateLeft,
            NagivateRight,
            NagivateUp,
            NagivateDown,
            NagivateSelect,
            NagivateBack,
            NagivateReturn,

            // Game action
            Walk,
            Run,
            Attack,
            UseAvatar,
            UseImpactFlare,
            UseFireShield,
            UseFlameExplosion,
            UseChaoticRift,
            UseHealthPotion,
            UseManaPotion,

            // System tray action
            OpenSkillScreen,
            OpenInventoryScreen,
            OpenQuestScreen,
            OpenPauseScreen,

            // Total action
            TotalActionCount,
        }


        /// <summary>
        /// Readable names of each action.
        /// </summary>
        private static readonly string[] actionNames = 
            {
                // Nagivate
                "Nagivate - Left",
                "Nagivate - Right",
                "Nagivate - Up",
                "Nagivate - Down",
                "Nagivate - Select",
                "Nagivate - Back",

                // Game action
                "Walk",
                "Run",
                "Attack",
                "Attack_Double",
                "Use skill - Avatar",
                "Use skill - Impact Flare",
                "Use skill - Fire Shield",
                "Use skill - Flame Explosion",
                "Use skill - Chaotic Rift",
                "Use potion - Health",
                "Use potion - Mana",

                // System tray
                "Screen - Skill",
                "Screen - Inventory",
                "Screen - Quest",
                "Screen - Pause",

                // Total
                "Total action"
            };

        /// <summary>
        /// Returns the readable name of the given action.
        /// </summary>
        public static string GetActionName(Action action)
        {
            int index = (int)action;

            if ((index < 0) || (index > actionNames.Length))
            {
                throw new ArgumentException("action");
            }

            return actionNames[index];
        }


        #endregion


        #region Support Types


        /// <summary>
        /// A combination of gamepad and keyboard keys mapped to a particular action.
        /// </summary>
        public class ActionMap
        {
            /// <summary>
            /// List of Keyboard controls to be mapped to a given action.
            /// </summary>
            public List<Keys> keyboardKeys = new List<Keys>();

            public WindowsPhoneGesture PhoneInput = new WindowsPhoneGesture();
        }

        public class WindowsPhoneGesture
        {
            public GestureType Type { get; set; }
            public GestureDirection Direction { get; set; }

            public bool IsBackGesture { get; set; }
            public bool IsStartGesture { get; set; }

            public WindowsPhoneGesture()
            {
                Type = GestureType.None;
                Direction = GestureDirection.None;
            }

        }

        #endregion


        #region Constants


        /// <summary>
        /// The value of an analog control that reads as a "pressed button".
        /// </summary>
        const float analogLimit = 0.5f;


        #endregion


        #region Keyboard Data


        /// <summary>
        /// The state of the keyboard as of the last update.
        /// </summary>
        private static KeyboardState currentKeyboardState;

        /// <summary>
        /// The state of the keyboard as of the last update.
        /// </summary>
        public static KeyboardState CurrentKeyboardState
        {
            get { return currentKeyboardState; }
        }


        /// <summary>
        /// The state of the keyboard as of the previous update.
        /// </summary>
        private static KeyboardState previousKeyboardState;


        /// <summary>
        /// Check if a key is pressed.
        /// </summary>
        public static bool IsKeyPressed(Keys key)
        {
            return currentKeyboardState.IsKeyDown(key);
        }

        public static bool IsPreKeyPressed(Keys key)
        {
            return previousKeyboardState.IsKeyDown(key);
        }

        /// <summary>
        /// Check if a key was just pressed in the most recent update.
        /// </summary>
        public static bool IsKeyTriggered(Keys key)
        {
            return (currentKeyboardState.IsKeyDown(key)) &&
                (!previousKeyboardState.IsKeyDown(key));
        }

        public static bool IsKeyHolded(Keys key)
        {
            return (previousKeyboardState.IsKeyUp(key) && IsKeyPressed(key));
            //return (currentKeyboardState.IsKeyDown(key)) &&
            //    (previousKeyboardState.IsKeyUp(key));
        }

        public static bool IsKeyPressedCombo(Keys key1, Keys key2)
        {
            return currentKeyboardState.IsKeyDown(key1) &&
                   currentKeyboardState.IsKeyDown(key2);
        }

        public static bool IsKeyPressedLongTime(Keys key, GameTime gameTime)
        {
            DateTime startTime = DateTime.Now;
            TimeSpan elapsedTime;
            int time = 0;
          
            if(currentKeyboardState.IsKeyDown(key) )
            {
                elapsedTime = (DateTime.Now - startTime);
                time = (int)elapsedTime.TotalMilliseconds;

            }
           

           return ( time >= 3000);


        }

        #endregion


        #region Mouse Data
        /// <summary>
        /// The state of the mouse as of the last update.
        /// </summary>
        private static MouseState currentMouseState;

        /// <summary>
        /// The state of the mouse as of the last update.
        /// </summary>
        public static MouseState CurrentMouseState
        {
            get { return currentMouseState; }
        }


        /// <summary>
        /// The state of the mouse as of the previous update.
        /// </summary>
        private static MouseState previousMouseState;


        /// <summary>
        /// Check if a mouse is pressed.
        /// </summary>
        public static bool IsLeftMousePressed()
        {
            return currentMouseState.LeftButton == ButtonState.Pressed;
        }


        /// <summary>
        /// Check if a mouse was just pressed in the most recent update.
        /// </summary>
        public static bool IsLeftMouseTriggered()
        {
            return currentMouseState.LeftButton == ButtonState.Pressed && !(previousMouseState.LeftButton == ButtonState.Pressed);
        }


        /// <summary>
        /// Get the current mouse position
        /// </summary>
        public static Vector2 GetMousePosition()
        {
            return new Vector2(currentMouseState.X, currentMouseState.Y);
        }
        #endregion


        #region Touch Data

        /// <summary>
        /// The state of the touch as of the last update.
        /// </summary>
        private static TouchCollection currentTouchState;

        /// <summary>
        /// The state of the touch as of the last update.
        /// </summary>
        public static TouchCollection CurrentTouchState
        {
            get { return currentTouchState; }
        }

        /// <summary>
        /// The state of the touch as of the previous update.
        /// </summary>
        private static TouchCollection previousTouchState;

        public static List<GestureSample> Gestures { get; set; }

        private static bool IsBackPressed()
        {
            return GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed;
        }

        private static bool IsStartPressed()
        {
            return GamePad.GetState(PlayerIndex.One).Buttons.Start == ButtonState.Pressed;
        }



        #endregion


        #region Action Mapping


        /// <summary>
        /// The action mappings for the game.
        /// </summary>
        private static ActionMap[] actionMaps;


        public static ActionMap[] ActionMaps
        {
            get { return actionMaps; }
        }


        /// <summary>
        /// Reset the action maps to their default values.
        /// </summary>
        private static void ResetActionMaps()
        {
            // Allocate
            actionMaps = new ActionMap[(int)Action.TotalActionCount];

            #region Nagivation
            actionMaps[(int)Action.NagivateLeft] = new ActionMap();
            actionMaps[(int)Action.NagivateLeft].keyboardKeys.Add(Keys.Left);
            actionMaps[(int)Action.NagivateLeft].PhoneInput.Direction = GestureDirection.Left;
            actionMaps[(int)Action.NagivateLeft].PhoneInput.Type = GestureType.HorizontalDrag;

            actionMaps[(int)Action.NagivateRight] = new ActionMap();
            actionMaps[(int)Action.NagivateRight].keyboardKeys.Add(Keys.Right);
            actionMaps[(int)Action.NagivateRight].PhoneInput.Direction = GestureDirection.Right;
            actionMaps[(int)Action.NagivateRight].PhoneInput.Type = GestureType.HorizontalDrag;

            actionMaps[(int)Action.NagivateUp] = new ActionMap();
            actionMaps[(int)Action.NagivateUp].keyboardKeys.Add(Keys.Up);
            actionMaps[(int)Action.NagivateUp].PhoneInput.Direction = GestureDirection.Up;
            actionMaps[(int)Action.NagivateUp].PhoneInput.Type = GestureType.VerticalDrag;

            actionMaps[(int)Action.NagivateDown] = new ActionMap();
            actionMaps[(int)Action.NagivateDown].keyboardKeys.Add(Keys.Down);
            actionMaps[(int)Action.NagivateDown].PhoneInput.Direction = GestureDirection.Down;
            actionMaps[(int)Action.NagivateDown].PhoneInput.Type = GestureType.VerticalDrag;

            actionMaps[(int)Action.NagivateSelect] = new ActionMap();
            actionMaps[(int)Action.NagivateSelect].keyboardKeys.Add(Keys.Enter);

            actionMaps[(int)Action.NagivateBack] = new ActionMap();
            actionMaps[(int)Action.NagivateBack].keyboardKeys.Add(Keys.Back);
            actionMaps[(int)Action.NagivateBack].PhoneInput.IsBackGesture = true;

            actionMaps[(int)Action.NagivateReturn] = new ActionMap();
            actionMaps[(int)Action.NagivateReturn].keyboardKeys.Add(Keys.Escape);
            actionMaps[(int)Action.NagivateReturn].PhoneInput.IsBackGesture = true;
            #endregion

            #region Game action
            actionMaps[(int)Action.Attack] = new ActionMap();
            actionMaps[(int)Action.Attack].keyboardKeys.Add(Keys.Space);
            actionMaps[(int)Action.Attack].keyboardKeys.Add(Keys.H);

            //actionMaps[(int)Action.Attack_Double] = new ActionMap();
            //actionMaps[(int)Action.Attack_Double].keyboardKeys.Add(Keys.Space);
            //actionMaps[(int)Action.Attack_Double].keyboardKeys.Add(Keys.H);

            actionMaps[(int)Action.Walk] = new ActionMap();
            actionMaps[(int)Action.Walk].keyboardKeys.Add(Keys.A);
            actionMaps[(int)Action.Walk].keyboardKeys.Add(Keys.S);
            actionMaps[(int)Action.Walk].keyboardKeys.Add(Keys.W);
            actionMaps[(int)Action.Walk].keyboardKeys.Add(Keys.D);

            actionMaps[(int)Action.Run] = new ActionMap();
            actionMaps[(int)Action.Run].keyboardKeys.Add(Keys.A);
            actionMaps[(int)Action.Run].keyboardKeys.Add(Keys.S);
            actionMaps[(int)Action.Run].keyboardKeys.Add(Keys.W);
            actionMaps[(int)Action.Run].keyboardKeys.Add(Keys.D);

            actionMaps[(int)Action.UseAvatar] = new ActionMap();
            actionMaps[(int)Action.UseAvatar].keyboardKeys.Add(Keys.D1);

            actionMaps[(int)Action.UseImpactFlare] = new ActionMap();
            actionMaps[(int)Action.UseImpactFlare].keyboardKeys.Add(Keys.L);

            actionMaps[(int)Action.UseFireShield] = new ActionMap();
            actionMaps[(int)Action.UseFireShield].keyboardKeys.Add(Keys.D3);

            actionMaps[(int)Action.UseFlameExplosion] = new ActionMap();
            actionMaps[(int)Action.UseFlameExplosion].keyboardKeys.Add(Keys.D4);

            actionMaps[(int)Action.UseChaoticRift] = new ActionMap();
            actionMaps[(int)Action.UseChaoticRift].keyboardKeys.Add(Keys.D5);

            actionMaps[(int)Action.UseHealthPotion] = new ActionMap();
            actionMaps[(int)Action.UseHealthPotion].keyboardKeys.Add(Keys.Q);

            actionMaps[(int)Action.UseManaPotion] = new ActionMap();
            actionMaps[(int)Action.UseManaPotion].keyboardKeys.Add(Keys.E);
            #endregion

            #region System tray
            actionMaps[(int)Action.OpenSkillScreen] = new ActionMap();
            actionMaps[(int)Action.OpenSkillScreen].keyboardKeys.Add(Keys.K);

            actionMaps[(int)Action.OpenInventoryScreen] = new ActionMap();
            actionMaps[(int)Action.OpenInventoryScreen].keyboardKeys.Add(Keys.I);

            actionMaps[(int)Action.OpenQuestScreen] = new ActionMap();
            actionMaps[(int)Action.OpenQuestScreen].keyboardKeys.Add(Keys.J);

            actionMaps[(int)Action.OpenPauseScreen] = new ActionMap();
            actionMaps[(int)Action.OpenPauseScreen].keyboardKeys.Add(Keys.Escape);
            actionMaps[(int)Action.OpenPauseScreen].PhoneInput.IsBackGesture = true;
            #endregion
        }


        /// <summary>
        /// Check if an action has been pressed.
        /// </summary>
        public static bool IsActionPressed(Action action)
        {
            return IsActionMapPressed(actionMaps[(int)action]);
        }

        /// <summary>
        /// Check if an action was just performed in the most recent update.
        /// </summary>
        public static bool IsActionTriggered(Action action)
        {
            return IsActionMapTriggered(actionMaps[(int)action]);
        }

        public static bool IsActionHolded(Action action)
        {
            return IsActionMapHolded(actionMaps[(int)action]);
        }



        /// <summary>
        /// Check if an action map has been pressed.
        /// </summary>
        private static bool IsActionMapPressed(ActionMap actionMap)
        {
            bool result = false;
            for (int i = 0; i < actionMap.keyboardKeys.Count; i++)
            {
                if (IsKeyPressed(actionMap.keyboardKeys[i]))
                {
                    result = true;
                }
            }

            // Handle Windows Phone gestures
            if (!result)
            {
                if (actionMap.PhoneInput.IsStartGesture)
                {
                    result = IsStartPressed();
                }
                else if (actionMap.PhoneInput.IsBackGesture)
                {
                    result = IsBackPressed();
                }
                else
                {
                    if (Gestures.Count > 0)
                    {
                        GestureSample gesture = Gestures[Gestures.Count - 1];
                        if (gesture.GestureType == actionMap.PhoneInput.Type)
                        {
                            if (gesture.GetDirection() == actionMap.PhoneInput.Direction
                                && gesture.GetDirection() == actionMap.PhoneInput.Direction)
                            {
                                InputManager.Gestures.Remove(gesture);
                                result = true;
                            }
                        }
                    }
                }
            }
            return result;
        }


        /// <summary>
        /// Check if an action map has been triggered this frame.
        /// </summary>
        private static bool IsActionMapTriggered(ActionMap actionMap)
        {
            bool result = false;
            for (int i = 0; i < actionMap.keyboardKeys.Count; i++)
            {
                if (IsKeyTriggered(actionMap.keyboardKeys[i]))
                {
                    result = true;
                }
            }

            // Handle Windows Phone gestures
            if (!result)
            {
                if (actionMap.PhoneInput.IsStartGesture)
                {
                    result = IsStartPressed();
                }
                else if (actionMap.PhoneInput.IsBackGesture)
                {
                    result = IsBackPressed();
                }
                else
                {
                    if (Gestures.Count > 0)
                    {
                        GestureSample gesture = Gestures[Gestures.Count - 1];
                        if (gesture.GestureType == actionMap.PhoneInput.Type
                            && gesture.GetDirection() == actionMap.PhoneInput.Direction)
                        {
                            InputManager.Gestures.Remove(gesture);
                            result = true;
                        }
                    }
                }
            }

            return result;
        }

        private static bool IsActionMapHolded(ActionMap actionMap)
        {
            bool result = false;
            for (int i = 0; i < actionMap.keyboardKeys.Count; i++)
            {
                if (IsKeyHolded(actionMap.keyboardKeys[i]))
                {
                    result = true;
                }
            }

            return result;
        }


        #endregion


        #region Initialization


        /// <summary>
        /// Initializes the default control keys for all actions.
        /// </summary>
        public static void Initialize()
        {
            TouchPanel.EnabledGestures = GestureType.Tap | GestureType.VerticalDrag | GestureType.HorizontalDrag
                | GestureType.Flick;
            ResetActionMaps();
            Gestures = new List<GestureSample>();
        }


        #endregion


        #region Updating


        /// <summary>
        /// Updates the keyboard and gamepad control states.
        /// </summary>
        public static void Update()
        {
            Gestures.Clear();

            // update the keyboard state
            previousKeyboardState = currentKeyboardState;
            currentKeyboardState = Keyboard.GetState();

            // update the mouse state
            previousMouseState = currentMouseState;
            currentMouseState = Mouse.GetState();

            // Update the touch state
            previousTouchState = currentTouchState;
            currentTouchState = TouchPanel.GetState();


            while (TouchPanel.IsGestureAvailable)
            {
                var gesture = TouchPanel.ReadGesture();
                Gestures.Add(gesture);
            }
        }


        #endregion


        public static bool IsButtonClicked(Rectangle[] positions)
        {
#if WINDOWS
            foreach (Rectangle rect in positions)
            {
                if (rect.Contains((int)currentMouseState.X, (int)currentMouseState.Y) &&
                    IsLeftMouseTriggered())
                {
                    return true;
                }
            }
#else
            foreach (var gesture in Gestures)
            {
                foreach (Rectangle rect in positions)
                {
                    if (rect.Contains((int)gesture.Position.X, (int)gesture.Position.Y))
                    {
                        Gestures.Remove(gesture);
                        return true;
                    }
                }
            }
#endif
            return false;
        }

        public static bool IsButtonClicked(Rectangle position)
        {
            return IsButtonClicked(new Rectangle[] { position });
        }

        public static bool IsButtonHold(Rectangle[] positions)
        {
#if WINDOWS
            foreach (Rectangle rect in positions)
            {
                if (rect.Contains((int)currentMouseState.X, (int)currentMouseState.Y) &&
                    IsLeftMousePressed())
                    return true;
            }
#else
            foreach (TouchLocation touch in currentTouchState)
            {
                foreach (Rectangle rect in positions)
                {
                    if (rect.Contains((int)touch.Position.X, (int)touch.Position.Y))
                        return true;
                }
            }
#endif
            return false;
        }

        public static bool IsButtonHold(Rectangle position)
        {
            return IsButtonHold(new Rectangle[] { position });
        }
    }

}
